﻿# coding: utf-8

import time
import wx

import Entity
import EntityTerrain
import EntityTerrainRegion
import TerrainHeightBrush
import TerrainLayerBrush
import Painter
import Material
import MPCullingMode
import MPAmbient
import MPDiffuse
import MPSpecular
import RenderDefine

from InputHandler import CInputHandler

BRUSH_WIDTH = 32
BRUSH_HEIGHT = 32

class CTerrainEditor(CInputHandler):
    """ Terrain editor used to edit the terrain entity """
    
    def __init__(self):
        """ Default class constructor """
        
        self._heightBrushDict = {}
        self._layerBrushDict = {}
        
        self._currentHeightBrushName = None
        self._currentLayerBrushName = None
        self._currentLayerName = None
        
        self._terrainNodeRef = None
        self._terrainCoreHandle = 0
        
        self._lastUpdate = 0
        self._leftButtonDown = False        
 
    def IsEditing(self):
        return self._terrainNodeRef and self._terrainNodeRef() and self._terrainCoreHandle
        
    def Create(self):
        """ Create the terrain editor """

        PANEL_TERRAIN_HEIGHT.LoadHeightBrush()
        PANEL_TERRAIN_LAYER.LoadLayerBrush()
        
    def Destroy(self):
        """ Destroy the terrain editor """
        
        for brushHandle in self._heightBrushDict.values():
            TerrainHeightBrush._Delete(brushHandle)
            
        self._heightBrushDict = {}
        
        for brushHandle in self._layerBrushDict.values():
            TerrainLayerBrush._Delete(brushHandle)
            
        self._layerBrushDict = {}
        
    def Render(self):
        """ Render terrain editor tools """
        
        if not self.IsEditing():
            return
            
        if WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_HEIGHT_PAINT:
            brushCoreHandle = self._heightBrushDict.get(self._currentHeightBrushName, None)
            if brushCoreHandle:            
                TerrainHeightBrush.Render(brushCoreHandle, self._terrainCoreHandle)
                Painter.Flush()
        elif WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_LAYER_PAINT:
            brushCoreHandle = self._layerBrushDict.get(self._currentLayerBrushName, None)
            if brushCoreHandle:            
                TerrainLayerBrush.Render(brushCoreHandle, self._terrainCoreHandle)
                Painter.Flush()

    def CreateTerrain(self, width, height, shaderFile, defaultLayerFile):
        """ Create a terrain scene node """
        
        # only allowed one terrain entity.
        if self._terrainNodeRef:
            return False
        
        # create terrain entity.
        self._terrainNodeRef, self._terrainCoreHandle = SCENE_EDITOR.CreateTerrainSceneNode(width, height, shaderFile, defaultLayerFile)
        if not self._terrainNodeRef:
            return False
            
        # setup the default terrain material parameters.
        materialHandle = EntityTerrain.GetMaterial(self._terrainCoreHandle)
        
        # culling mode.
        cullingModeHandle = Material.GetCullingModeProperty(materialHandle)
        MPCullingMode.SetCullingMode(cullingModeHandle, RenderDefine.CM_CCW)
        
        # material ambient
        ambientHandle = Material.GetAmbientProperty(materialHandle)
        MPAmbient.SetAmbient(ambientHandle, True, 0.5, 0.5, 0.5, 1.0)
        
        # material diffuse
        diffuseHandle = Material.GetDiffuseProperty(materialHandle)
        MPDiffuse.SetDiffuse(diffuseHandle, True, 0.5, 0.5, 0.5, 1.0)
        
        # material specular
        specularHandle = Material.GetSpecularProperty(materialHandle)
        MPSpecular.SetSpecular(specularHandle, True, 0.5, 0.5, 0.5, 1.0, 30.0)
        
        return True
        
    def GetTerrainMaterialHandle(self):
        if not self.IsEditing():
            return None
            
        return EntityTerrain.GetMaterial(self._terrainCoreHandle)
        
    def AddTerrainHeightBrush(self, fileName):
        """ Add terrain height brush from file. """
        
        if self._heightBrushDict.get(fileName, None):
            return False
            
        brushHandle = TerrainHeightBrush._New()
        if not brushHandle:
            return False
            
        if not TerrainHeightBrush.LoadImage(brushHandle, fileName, BRUSH_WIDTH, BRUSH_HEIGHT):
            TerrainHeightBrush._Delete(brushHandle)
            return False
            
        self._heightBrushDict[fileName] = brushHandle
        TerrainHeightBrush.SetSize(brushHandle, 600)
        TerrainHeightBrush.SetPowerFactor(brushHandle, 0.2)
        TerrainHeightBrush.SetPowerOffset(brushHandle, 0)
        return True

    def AddTerrainLayerBrush(self, fileName):
        """ Add terrain layer brush from file. """
        
        if self._layerBrushDict.get(fileName, None):
            return False
            
        brushHandle = TerrainLayerBrush._New()
        if not brushHandle:
            return False
            
        if not TerrainLayerBrush.LoadImage(brushHandle, fileName, BRUSH_WIDTH, BRUSH_HEIGHT):
            TerrainLayerBrush._Delete(brushHandle)
            return False
            
        self._layerBrushDict[fileName] = brushHandle
        TerrainLayerBrush.SetSize(brushHandle, 600)
        TerrainLayerBrush.SetPowerFactor(brushHandle, 1.0)
        return True
                
    def RemoveTerrainHeightBrush(self, name):
        """ Remove a terrain height brush """
        
        brushHandle = self._heightBrushDict.get(name, None)
        if not brushHandle:
            return False
            
        TerrainHeightBrush._Delete(brushHandle)
        self._heightBrushDict.pop(name)
        return True
    
    def RemoveTerrainLayerBrush(self, name):
        """ Remove a terrain layer brush """
        
        brushHandle = self._layerBrushDict.get(name, None)
        if not brushHandle:
            return False
            
        TerrainLayerBrush._Delete(brushHandle)
        self._layerBrushDict.pop(name)
        return True
                
    def UseHeightBrush(self, name):
        """ Set the name of current terrain height brush """
        
        self._currentHeightBrushName = name
        
    def UseLayerBrush(self, name):
        """ Set the name of current terrain layer brush """
        
        self._currentLayerBrushName = name
        
    def UseLayer(self, name):
        """ Set the name of current layer """
        
        self._currentLayerName = name
        
    def GetCurrentHeightBrushHandle(self):
        """ Get core handle of current terrain height brush """
        
        if self.IsEditing():
            return self._heightBrushDict.get(self._currentHeightBrushName, None)

        return None
        
    def GetCurrentLayerBrushHandle(self):
        """ Get core handle of current terrain layer brush """
        
        if self.IsEditing():
            return self._layerBrushDict.get(self._currentLayerBrushName, None)
            
        return None
        
    def OnMouseLeftDown(self, e):            
        self._leftButtonDown = True
        
    def OnMouseLeftUp(self, e):
        self._leftButtonDown = False

    def OnKeyDown(self, e):           
        if not self.IsEditing():
            return   
                    
        if WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_HEIGHT_PAINT:
            brushCoreHandle = self._heightBrushDict.get(self._currentHeightBrushName, None)
            if not brushCoreHandle:
                return            
                
            code = e.GetKeyCode()
            if code == wx.WXK_SHIFT:
                TerrainHeightBrush.SetPaintUp(brushCoreHandle, True)
                PANEL_TERRAIN_HEIGHT.RefrehBrushParam()
            elif code == wx.WXK_CONTROL:
                TerrainHeightBrush.SetPaintUp(brushCoreHandle, False)
                PANEL_TERRAIN_HEIGHT.RefrehBrushParam()
                
    def OnMouseMotion(self, e):
        if not self.IsEditing():
            return
            
        x, y = e.GetPosition()
        ret, ix, iy, iz = EntityTerrain.Pick(self._terrainCoreHandle, x, y)
                        
        if WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_HEIGHT_PAINT:
            brushCoreHandle = self._heightBrushDict.get(self._currentHeightBrushName, None)
            if not brushCoreHandle:
                return
                
            TerrainHeightBrush.SetPosition(brushCoreHandle, ix, iz)
            
        elif WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_LAYER_PAINT:
            brushCoreHandle = self._layerBrushDict.get(self._currentLayerBrushName, None)
            if not brushCoreHandle:
                return
                
            TerrainLayerBrush.SetPosition(brushCoreHandle, ix, iz)
            
    def __DoPaint(self, screen_x, screen_y):
        if not self.IsEditing():
            return   

        # Get the collision point
        ret, x, y, z = EntityTerrain.Pick(self._terrainCoreHandle, screen_x, screen_y)
        
        if WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_HEIGHT_PAINT:
            brushCoreHandle = self._heightBrushDict.get(self._currentHeightBrushName, None)
            if brushCoreHandle:
                TerrainHeightBrush.SetPosition(brushCoreHandle, x, z)
                EntityTerrain.PaintHeight(self._terrainCoreHandle, brushCoreHandle)
        elif WORLD_EDITOR._currentOp == OPD.OP_TERRAIN_LAYER_PAINT:
            brushCoreHandle = self._layerBrushDict.get(self._currentLayerBrushName, None)
            if brushCoreHandle:
                TerrainLayerBrush.SetPosition(brushCoreHandle, x, z)
                EntityTerrain.PaintLayer(self._terrainCoreHandle, brushCoreHandle, self._currentLayerName)

    def Update(self, dt):
        if time.clock() - self._lastUpdate < 0.1:
            return

        self._lastUpdate = time.clock()
        if self._leftButtonDown:
            p = wx.GetMousePosition()
            p = PANEL_EDITOR_SCENE.ScreenToClient(p)
            self.__DoPaint(p[0], p[1])      
                            
__builtins__['TERRAIN_EDITOR'] = CTerrainEditor()
